package com.cans.lightning.business.system.service.impl;

import com.cans.lightning.base.mapper.IBaseMapper;
import com.cans.lightning.base.service.impl.BaseServiceImpl;
import com.cans.lightning.business.system.dao.CodeGenerateConfigDao;
import com.cans.lightning.business.system.dto.CodeGenerateConfigDto;
import com.cans.lightning.business.system.entity.CodeGenerateConfig;
import com.cans.lightning.business.system.enums.ResetRuleEnum;
import com.cans.lightning.business.system.mapper.CodeGenerateConfigMapperI;
import com.cans.lightning.business.system.service.api.ICodeGenerateConfigService;
import com.cans.lightning.utils.StringUtils;
import org.beetl.sql.mapper.BaseMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.time.ZonedDateTime;
import java.time.temporal.WeekFields;
import java.util.Objects;

/**
 * 编码规则配置
 *
 * @author shencan
 * @date 2020/6/14 20:53
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class CodeGenerateConfigServiceImpl extends BaseServiceImpl<CodeGenerateConfig, CodeGenerateConfigDto, String> implements ICodeGenerateConfigService {

    @Resource
    private CodeGenerateConfigDao codeGenerateConfigDao;
    @Resource
    private CodeGenerateConfigMapperI codeGenerateConfigMapper;


    @Override
    public BaseMapper<CodeGenerateConfig> getDaoImpl() {
        return codeGenerateConfigDao;
    }

    @Override
    public IBaseMapper<CodeGenerateConfig, CodeGenerateConfigDto, String> getMapperImpl() {
        return codeGenerateConfigMapper;
    }

    @Override
    public CodeGenerateConfig getByCode(String code) {
        return codeGenerateConfigDao.createLambdaQuery().andEq(CodeGenerateConfig::getCode, code).single();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public String generateCode(String code) {

        String resCode = "";

        CodeGenerateConfig config = this.getByCode(code);
        if (config == null) {
            throw new RuntimeException("编号:[" + code + "]未查询到对应的配置项");
        }

        // 检查并重置序号
        if (this.checkReset(config)) {
            this.resetCode(config);
        }

        // 前缀
        String prefix = config.getPrefix();

        if (!StringUtils.isEmpty(prefix)) {
            resCode += prefix;
        }
        // 年
        ZonedDateTime now = ZonedDateTime.now();

        if (config.getYear() != null) {
            resCode += now.getYear();
        }

        // 月
        if (config.getMonth() != null) {
            resCode += String.format("%02d", now.getMonthValue());
        }
        // 日
        if (config.getDay() != null) {
            resCode += String.format("%02d", now.getDayOfMonth());
        }
        // 当前序号
        BigInteger currentNumber = config.getCurrentNumber();
        String formatCurrent = String.format("%0" + config.getNumberLength() + "d", currentNumber);
        resCode += formatCurrent;

        // 修改当前编号
        config.setCurrentNumber(currentNumber.add(BigInteger.valueOf(config.getIncrement())));
        config.setLastGenerateDateTime(ZonedDateTime.now());
        // 保存
        this.saveOrUpdate(config);

        return resCode;
    }


    @Override
    public void saveOrUpdateDto(CodeGenerateConfigDto dto) {
        if (StringUtils.isEmpty(dto.getId())) {
            dto.setCurrentNumber(dto.getInitNumber());
        } else {
            CodeGenerateConfig generateConfig = this.getById(dto.getId());
            // 年
            generateConfig.setYear(dto.getYear());
            // 月
            generateConfig.setMonth(dto.getMonth());
            // 日
            generateConfig.setDay(dto.getDay());
            // 增量
            generateConfig.setIncrement(dto.getIncrement());
            // 重置规则
            generateConfig.setResetRule(dto.getResetRule());
            // 流水号长度
            generateConfig.setNumberLength(dto.getNumberLength());
            // 前缀
            generateConfig.setPrefix(dto.getPrefix());
            // 保存
            this.saveOrUpdate(generateConfig);
            codeGenerateConfigMapper.toDto(generateConfig);
        }
    }

    /**
     * 校验并重置编号
     *
     * @param config
     */
    private boolean checkReset(CodeGenerateConfig config) {

        boolean needReset = false;

        ZonedDateTime lastGenerateDateTime = config.getLastGenerateDateTime();
        ZonedDateTime now = ZonedDateTime.now();

        Integer resetRule = config.getResetRule();

        if (ResetRuleEnum.getByKey(resetRule) == null) {
            return false;
        }

        switch (Objects.requireNonNull(ResetRuleEnum.getByKey(resetRule))) {
            // 总是
            case ALWAYS:
                needReset = true;
                break;
            // 从不
            case NEVER:
                needReset = false;
                break;
            // 每天
            case DAY:
                if (lastGenerateDateTime.getYear() != now.getYear() ||
                        lastGenerateDateTime.getDayOfYear() != now.getDayOfYear()) {
                    needReset = true;
                }
                break;
            // 每周
            case WEEK:
                WeekFields weekFields = WeekFields.ISO;
                if (lastGenerateDateTime.getYear() != now.getYear() ||
                        lastGenerateDateTime.get(weekFields.weekOfWeekBasedYear()) != now.get(weekFields.weekOfWeekBasedYear())) {

                    needReset = true;
                }
                break;
            // 每月
            case MONTH:
                if (lastGenerateDateTime.getYear() != now.getYear() ||
                        lastGenerateDateTime.getMonthValue() != now.getMonthValue()) {
                    needReset = true;
                }
                break;
            // 每年
            case YEAR:
                if (lastGenerateDateTime.getYear() != now.getYear()) {
                    needReset = true;
                }
                break;

            default:
                break;
        }

        return needReset;
    }

    /**
     * 重置序号
     *
     * @param config 配置实体
     * @return
     */
    private CodeGenerateConfig resetCode(CodeGenerateConfig config) {

        config.setCurrentNumber(config.getInitNumber());

        return config;
    }
}
